package com.hontlong.baidump3crawler;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.script.ScriptException;
import javax.sql.DataSource;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import org.apache.log4j.Logger;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import com.wole.commons.iterator.MinMaxBatchIterator;
import com.wole.commons.script.JsScriptEngine;
import com.wole.commons.string.HtmlCleanerXpathUtil;

public class BaiduMp3Searcher {

	private static final Logger logger = Logger.getLogger(BaiduMp3Searcher.class);

	final static HtmlCleaner cleaner = new HtmlCleaner();

	/**
	 * @param args
	 */
	public static void main(final String[] args) {
		if (logger.isDebugEnabled()) {
			logger.debug("main(String[]) - start");
		}

		final Resource res = new ClassPathResource("baidump3searcher.xml");
		final BeanFactory bf = new XmlBeanFactory(res);
		final BaiduMp3Searcher crawler = (BaiduMp3Searcher) bf.getBean("start");

		crawler.work();

		if (logger.isDebugEnabled()) {
			logger.debug("main(String[]) - end");
		}
	}

	private DataSource dataSource;

	private QueryRunner queryRunner;

	private String decode(String sertim, String coding) {
		JsScriptEngine js = new JsScriptEngine();
		js.setScript("function decode(url){\r\n" + "var len = url.length;\r\n"
			+ "var decurl = \"\";\r\n" + "var asc_arr1 = [], asc_arr2 = [];\r\n" + "\r\n"
			+ "var key = sertim % 26;\r\n" + "key = key ? key : 1;\r\n" + "\r\n"
			+ "function init(head, bottom, middle){\r\n"
			+ "	for (var i = head; i <= bottom; i++) {\r\n" + "		asc_arr1[i] = i + middle;\r\n"
			+ "		asc_arr2[i + middle] = i;\r\n" + "	}\r\n" + "}\r\n" + "\r\n"
			+ "init(0, 9, 48);\r\n" + "init(10, 35, 55);\r\n" + "init(36, 61, 61);\r\n" + "\r\n"
			+ "for (var i = 0; i < len; i++) {\r\n" + "	var word = url.charAt(i);\r\n" + "\r\n"
			+ "	if (/[A-Za-z0-9]/.test(word)) {\r\n"
			+ "		var pos = asc_arr2[url.charCodeAt(i)] - key;\r\n" + "		if (pos < 0)\r\n"
			+ "			pos += 62;\r\n" + "		word = String.fromCharCode(asc_arr1[pos]);\r\n" + "	}\r\n"
			+ "	decurl += word;\r\n" + "}\r\n" + "\r\n" + "return decurl;\r\n" + "}");
		js.put("sertim", sertim);
		try {
			Object retu = js.call("decode", coding);
			return (String) retu;
		} catch (ScriptException e) {
			logger.error("", e);
		}
		return null;
	}

	private boolean exists(String name, String singer) throws SQLException {
		String retu =
			(String) queryRunner.query(
				"select 'true' from t_mp3 where name=? and singer=? limit 1", new ScalarHandler(),
				name, singer);
		return "true".equals(retu);
	}

	public final DataSource getDataSource() {
		return dataSource;
	}

	public void init() {
		this.queryRunner = new QueryRunner(this.dataSource);
	}

	private void save(String name, String singer, URL url) throws SQLException {
		queryRunner.update("insert ignore into t_mp3 (name,singer,url)values(?,?,?)", name, singer,
			url.toExternalForm());
	}

	/**
	 * 找不到返回null
	 * 
	 * @param name
	 * @param singer
	 * @return
	 * @throws IOException
	 */
	private URL search(String name, String singer) throws IOException {
		// http://mp3.baidu.com/m?oq=%B0%C1%B9%C7+t&rsp=0&f=3&tn=baidump3&ct=134217728&lf=&rn=&lm=-1&word=%B0%C1%B9%C7+%CC%B7%D3%BD%F7%EB
		String word = name + " " + singer;
		String query =
			"http://mp3.baidu.com/m?oq=%B0%C1%B9%C7+t&rsp=0&f=3&tn=baidump3&ct=134217728&lf=&rn=&lm=0&word=";
		try {
			query += URLEncoder.encode(word, "GBK");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
		TagNode doc = cleaner.clean(new URL(query), "GBK");
		TagNode[] trs =
			HtmlCleanerXpathUtil.xpathArray(doc, "/body/div[2]/table[2]/tbody/tr[position()>1]");
		for (TagNode tr : trs) {
			TagNode a = HtmlCleanerXpathUtil.xpathOne(tr, "/td[2]/a");
			String href = a.getAttributeByName("href");
			href = href.replace(" ", "%20");
			// 对此页面做跟进解析
			String mp3UrlHtml = com.wole.commons.io.Utils.getHttpContent(href);
			// var sertim = 1275381587
			Pattern varP = Pattern.compile("var sertim = (\\d+)");
			Matcher m = varP.matcher(mp3UrlHtml);
			if (m.find()) {
				String sertim = m.group(1);
				Pattern funP = Pattern.compile("var\\s+encurl\\s+=\\s*\"?(.*?)\"");
				m = funP.matcher(mp3UrlHtml);
				if (m.find()) {
					String coding = m.group(1);
					href = decode(sertim, coding);
					if (href != null) {
						return new URL(href);
					}
				}
			}
		}
		return null;
	}

	public final void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	/** 业务主函数 */
	public void work() {
		MinMaxBatchIterator<Map<String, Object>> mp3It =
			new MinMaxBatchIterator<Map<String, Object>>();
		mp3It.setQueryRunner(queryRunner);
		/*
		 * mp3It.setMinIdSql("select min(id) from t_mp3_top_all"); mp3It.setMaxIdSql("select max(id) from
		 * t_mp3_top_all"); mp3It.setSql("select name,singer from t_mp3_top_all where id between ? and ?");
		 */
		/*
		 * mp3It.setMinIdSql("select min(id) from t_mp3_top_100"); mp3It.setMaxIdSql("select max(id) from
		 * t_mp3_top_100"); mp3It.setSql("select name,singer from t_mp3_top_100 where id between ? and ?");
		 */
		mp3It.setMinIdSql("select min(id) from t_mp3_top_500");
		mp3It.setMaxIdSql("select max(id) from t_mp3_top_500");
		mp3It.setSql("select name,singer from t_mp3_top_500 where id between ? and ?");

		mp3It.setBatchSize(100);
		mp3It.setHandler(new MapListHandler());

		try {
			for (Map<String, Object> mp3 : mp3It) {
				String name = (String) mp3.get("name");
				String singer = (String) mp3.get("singer");
				logger.info("处理:{" + name + "," + singer + "}");
				if (exists(name, singer)) {
					logger.info("已经有了");
					continue;
				}
				try {
					URL url = search(name, singer);
					if (url != null) {
						logger.info("获取url:" + url);
						save(name, singer, url);
					}
				} catch (IOException e) {
					logger.error("", e);
				} catch (SQLException e) {
					logger.error("", e);
				}
			}
		} catch (SQLException e) {
			logger.error("", e);
		}

	}
}
